<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta http-equiv="Content-Language" content="zh-CN"><title>daemon
  中文手册 [金步国]</title><style>
@font-face { font-family: "JinBuGuoWebMono"; src: url("http://www.jinbuguo.com/d/mono.ttf") format("truetype"); }
* { font-family: "JinBuGuoWebMono", "Ubuntu Mono", "Consolas", "Menlo", monospace; }
body { margin:10px; }
h1 { text-align:center; background:#ddd; }
h2#auth_name { text-align:center; margin: 10px 5%; }

    a.headerlink {
      color: #c60f0f;
      font-size: 0.8em;
      padding: 0 4px 0 4px;
      text-decoration: none;
      visibility: hidden;
    }

    a.headerlink:hover {
      background-color: #c60f0f;
      color: white;
    }

    h1:hover > a.headerlink, h2:hover > a.headerlink, h3:hover > a.headerlink, dt:hover > a.headerlink {
      visibility: visible;
    }
</style><script> var _hmt = _hmt || []; (function() { var hm = document.createElement("script"); hm.src = "https://hm.baidu.com/hm.js?d286c55b63a3c54a1e43d10d4c203e75"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s); })(); </script></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><h1>daemon 中文手册</h1><h2 id="auth_name">译者：<strong><a href="../index.html">金步国</a></strong></h2><hr><h3>版权声明</h3><p>本文译者是一位开源理念的坚定支持者，所以本文虽然不是软件，但是遵照开源的精神发布。</p><ul><li>无担保：本文译者不保证译文内容准确无误，亦不承担任何由于使用此文档所导致的损失。</li><li>自由使用：任何人都可以自由的<u>阅读/链接/打印</u>此文档，无需任何附加条件。</li><li>名誉权：任何人都可以自由的<u>转载/引用/再创作</u>此文档，但必须保留译者署名并注明出处。</li></ul><h3>其他作品</h3><p>本文译者十分愿意与他人分享劳动成果，如果你对我的其他翻译作品或者技术文章有兴趣，可以在如下位置查看现有的作品集：</p><ul><li><a href="../index.html">金步国作品集</a> [ <a href="../index.html">http://www.jinbuguo.com/</a> ]</li></ul><h3>联系方式</h3><p>由于译者水平有限，因此不能保证译文内容准确无误。如果你发现了译文中的错误(哪怕是错别字也好)，请来信指出，任何提高译文质量的建议我都将虚心接纳。</p><ul><li>Email(QQ)：70171448在QQ邮箱</li></ul><hr><a href="systemd.index.html">手册索引</a> ·
  <a href="systemd.directives.html">指令索引</a><span style="float:right">systemd-241</span><hr><div class="refentry"><a name="daemon"></a><div class="titlepage"></div><div class="refnamediv"><h2>名称</h2><p>daemon — 编写与打包系统守护进程</p></div><div class="refsect1"><a name="id-1.4"></a><h2 id="描述">描述<a class="headerlink" title="Permalink to this headline" href="daemon.html#%E6%8F%8F%E8%BF%B0">¶</a></h2><p>"守护进程"的意思是
    在后台运行的服务进程，
    常用于监督系统的运行或者提供某种功能。
    在传统的 SysV Unix 系统上，
    多个守护进程必须严格按照特定的顺序依次启动。
    在"新型"的
    <a href="systemd.html#"><span class="citerefentry"><span class="refentrytitle">systemd</span>(1)</span></a> 系统上，
    守护进程的启动顺序非常简单且非常强大。
    本手册同时涵盖了上述两种不同的启动方案，
    并特别推荐了应该包含在 systemd 系统中的守护进程。</p><div class="refsect2"><a name="id-1.4.3"></a><h3 id="传统的SysV守护进程">传统的SysV守护进程<a class="headerlink" title="Permalink to this headline" href="daemon.html#%E4%BC%A0%E7%BB%9F%E7%9A%84SysV%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B">¶</a></h3><p>传统的SysV
      守护进程
      在启动的时候，
      应该在初始化阶段
      执行下面的步骤：</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>关闭除
         STDIN
         STDOUT
         STDERR
        之外的
        所有文件描述符</p></li><li class="listitem"><p>
        重置所有信号处理器
        </p></li><li class="listitem"><p>
        重置所有信号掩码
        </p></li><li class="listitem"><p>
        清理环境变量(重置一部分，移除一部分)
        </p></li><li class="listitem"><p>调用 <code class="function">fork()</code> 创建一个后台进程
        </p></li><li class="listitem"><p>在子进程中调用
        <code class="function">setsid()</code> 从终端脱离并创建一个独立的会话
        </p></li><li class="listitem"><p>在子进程中再一次调用 <code class="function">fork()</code>
        以确保守护进程永远无法获取任何终端。
        </p></li><li class="listitem"><p>
        第一个子进程主动退出，
        只有第二个子进程(实际的守护进程)保持运行，
        并且以 init(PID=1) 为父进程。
        </p></li><li class="listitem"><p>
        守护进程(第二个子进程)将 STDIN STDOUT STDERR 连接到 <code class="filename">/dev/null</code> 虚拟设备
        </p></li><li class="listitem"><p>
        守护进程将 umask 设为 0
        </p></li><li class="listitem"><p>
        守护进程将当前目录切换到根目录(/)
        </p></li><li class="listitem"><p>
        守护进程将自身的PID记录到例如 <code class="filename">/run/foobar.pid</code> 这样的文件中
        </p></li><li class="listitem"><p>守护进程丢弃自己不需要的权限
        (如果可以)</p></li><li class="listitem"><p>
        守护进程通知最初的父进程：初始化工作已完成
        </p></li><li class="listitem"><p>
        最初的父进程自身退出
        </p></li></ol></div><p>注意，这些步骤对于下文讲述的新型守护进程是不需要的，
      除非为了刻意兼容传统的SysV系统。</p></div><div class="refsect2"><a name="id-1.4.4"></a><h3 id="新型守护进程">新型守护进程<a class="headerlink" title="Permalink to this headline" href="daemon.html#%E6%96%B0%E5%9E%8B%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B">¶</a></h3><p>Linux 系统上的新型守护进程更容易被监控也更容易实现。
      守护进程无需实现前文所描述的复杂步骤，
      即可直接在 systemd 提供的干净的上下文环境中运行：</p><p>环境变量已经被清理、信号处理器与信号掩码已经被重置、
      没有遗留的文件描述符、守护进程自动在其专属的会话中执行、
      标准输入(STDIN)已被连接到 <code class="filename">/dev/null</code> 虚拟设备(除非另有配置)、
      标准输出(STDOUT)与标准错误(STDERR)已被连接到
      <a href="systemd-journald.service.html#"><span class="citerefentry"><span class="refentrytitle">systemd-journald.service</span>(8)</span></a>
      日志服务(除非另有配置)、umask 已经被重置 … 等等
      </p><p>新型守护进程
      只需要遵守如下要求：</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>收到 <code class="constant">SIGTERM</code> 信号后
        关闭进程并确保干净的退出</p></li><li class="listitem"><p>收到 <code class="constant">SIGHUP</code> 信号后
        重新加载配置文件
        (若需要)</p></li><li class="listitem"><p>主守护进程
        在退出时
        应该按照
        <a class="ulink" href="http://refspecs.linuxbase.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/iniscrptact.html" target="_top">LSB
        recommendations for SysV init
        scripts</a> 的要求返回恰当的退出码，以便于 systemd 判断服务的退出状态。</p></li><li class="listitem"><p>若可行，在初始化的最后一步，
        通过 D-Bus 创建进程的控制接口，
        并在 D-Bus 上注册一个总线名称。</p></li><li class="listitem"><p>提供一个
        <code class="filename">.service</code> 单元文件，
        包含如何启动/停止/维护该服务的配置。
        详见
        <a href="systemd.service.html#"><span class="citerefentry"><span class="refentrytitle">systemd.service</span>(5)</span></a>
        手册。</p></li><li class="listitem"><p>尽可能
        依赖于
         systemd
        的资源控制与权限剥夺功能
        (CPU与内存占用/文件访问等等)，
        而不要自己实现它们。
        详见
        <a href="systemd.exec.html#"><span class="citerefentry"><span class="refentrytitle">systemd.exec</span>(5)</span></a>
        手册。</p></li><li class="listitem"><p>若使用了 D-Bus ，
        则强烈推荐使用基于 D-Bus 的启动机制。
        这样做有许多好处：
        (1)守护进程可以按需延迟启动；
        (2)可以和依赖于它的进程并行启动
        (提升启动速度)；
        (3)守护进程可以在失败时被自动重启
        而不丢失D-Bus总线上的请求
        (详见下文)。</p></li><li class="listitem"><p>若守护进程通过套接字提供服务，
        则强烈推荐使用
        基于套接字的启动机制(详见下文)。
        这样做有许多好处：
        (1)守护进程可以按需延迟启动；
        (2)可以和依赖于它的进程并行启动(提升启动速度)；
        (3)对于无状态协议(例如 syslog, DNS)，
        守护进程可以在失败时被自动重启
        而不丢失套接字上的请求(详见下文)</p></li><li class="listitem"><p>若可能，
        守护进程应该通过
        <a href="http://www.jinbuguo.com/systemd/sd_notify.html#"><span class="citerefentry"><span class="refentrytitle">sd_notify</span>(3)</span></a>
        接口通知 systemd "启动已完成"或"状态已更新"这样的消息。</p></li><li class="listitem"><p>不要使用
        <code class="function">syslog()</code> 记录日志，
        只需简单的使用
         <code class="function">fprintf()</code>
        向 STDERR 输出日志即可。
        如果必须指明日志等级，
        则可以在日志的
        行首加上类似
        "<code class="literal">&lt;4&gt;</code>"
        这样的前缀即可(这里表示4级"WARNING")。
        详见
        <a href="http://www.jinbuguo.com/systemd/sd-daemon.html#"><span class="citerefentry"><span class="refentrytitle">sd-daemon</span>(3)</span></a>
        与
        <a href="systemd.exec.html#"><span class="citerefentry"><span class="refentrytitle">systemd.exec</span>(5)</span></a> 手册。</p></li></ol></div><p>上述要求与
      <a class="ulink" href="https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html" target="_top">
      Apple MacOS X Daemon Requirements</a> 类似，
      但并不完全相同。</p></div></div><div class="refsect1"><a name="id-1.5"></a><h2 id="启动">启动<a class="headerlink" title="Permalink to this headline" href="daemon.html#%E5%90%AF%E5%8A%A8">¶</a></h2><p>systemd 提供了
    多种启动机制(见下文)，
    而服务单元也经常同时使用其中的几种。
    例如
    <code class="filename">bluetoothd.service</code>
    可以在插入蓝牙硬件时被启动，
    也可以在某进程访问其 D-Bus 接口时被启动。
    又如打印服务可以在IPP端口有流量接入时被启动，
    也可以在插入打印机硬件时被启动，
    还可以在有文件进入打印机 spool 目录时被启动。
    甚至对于必须在系统启动时无条件启动的服务，
    为了尽可能并发启动，
    也应该使用某些启动机制。
    如果某守护进程实现了一个 D-Bus 服务或者监听一个套接字，
    那么使用基于 D-Bus 或基于套接字的启动机制，
    将允许该进程与其客户端同时并行启动(从而加快启动速度)。
    因为所有的通信渠道都已事先建立，
    并且不会丢失任何客户端请求，
    同时 D-Bus 总线或者内核会将客户端请求排入队列等候，
    直到完成启动。</p><div class="refsect2"><a name="id-1.5.3"></a><h3 id="系统启动时启动">系统启动时启动<a class="headerlink" title="Permalink to this headline" href="daemon.html#%E7%B3%BB%E7%BB%9F%E5%90%AF%E5%8A%A8%E6%97%B6%E5%90%AF%E5%8A%A8">¶</a></h3><p>传统的守护进程一般是在系统启动时通过SysV初始化脚本自动启动，
      systemd 也支持这种启动方式。
      对于 systemd 来说，如果希望确保某单元在系统启动时自动启动，
      那么最佳的做法是在默认启动目标
      (通常是 <code class="filename">multi-user.target</code> 或 <code class="filename">graphical.target</code>)的
      <code class="filename">.wants/</code> 目录中为该单元建立软链接。
      参见
      <a href="systemd.unit.html#"><span class="citerefentry"><span class="refentrytitle">systemd.unit</span>(5)</span></a>
      手册以了解 <code class="filename">.wants/</code> 目录，
      参见
      <a href="systemd.special.html#"><span class="citerefentry"><span class="refentrytitle">systemd.special</span>(7)</span></a>
      手册以了解上述两个特殊的启动目标。</p></div><div class="refsect2"><a name="id-1.5.4"></a><h3 id="基于套接字的启动">基于套接字的启动<a class="headerlink" title="Permalink to this headline" href="daemon.html#%E5%9F%BA%E4%BA%8E%E5%A5%97%E6%8E%A5%E5%AD%97%E7%9A%84%E5%90%AF%E5%8A%A8">¶</a></h3><p>为了尽可能提高并行性与健壮性，
      以及简化配置与开发，
      对于需要监听套接字的服务，
      强烈推荐使用基于套接字的启动机制。
      使用此机制后，
      守护进程不再需要创建和绑定套接字，
      而是由 systemd 接管这个工作。
      systemd 将会根据单元文件的设置，
      预先创建所需的套接字，
      并在第一个客户端请求接入的时候
      启动该服务，
      以实现服务的按需启动。
      该机制的好处还在于，
      预先创建好套接字之后，
      所有使用此套接字通信的进程可以并行启动(包括客户端和服务端)。
      此外，重启服务只会导致丢失最低限度的客户端连接，
      甚至不丢失任何客户端请求
      (例如对于 DNS 或 syslog 这样的无状态协议)。
      因为套接字在服务重启期间
      始终保持有效并且可被访问，
      同时所有客户端请求
      也都被排入队列等候处理。</p><p>使用此机制之后，
      守护进程必须要从 systemd 接收已创建好的套接字，
      而不能自己创建并绑定套接字。
      关于如何使用该机制，参见
      <a href="http://www.jinbuguo.com/systemd/sd_listen_fds.html#"><span class="citerefentry"><span class="refentrytitle">sd_listen_fds</span>(3)</span></a>
      与
      <a href="http://www.jinbuguo.com/systemd/sd-daemon.html#"><span class="citerefentry"><span class="refentrytitle">sd-daemon</span>(3)</span></a>
      手册。
      只需要小小的修改，
      即可在原有启动机制的基础上
      添加基于套接字的启动机制，
      至于如何移植，
      详见后文。</p><p>systemd 通过
      <code class="filename">.socket</code> 单元实现该机制，详见
      <a href="systemd.socket.html#"><span class="citerefentry"><span class="refentrytitle">systemd.socket</span>(5)</span></a>
      手册。
      必须确保所有为支持基于套接字启动而创建的监听 socket 单元都被包含在
      <code class="filename">sockets.target</code> 中。
      建议在 socket 单元的
      "<code class="literal">[Install]</code>" 小节加入
      <code class="varname">WantedBy=sockets.target</code> 设置，
      以确保在启用该单元时能够自动添加上述依赖关系。
      除非明确设置了 <code class="varname">DefaultDependencies=no</code> ，
      否则会为所有 socket 单元隐含的创建必要的顺序依赖。
      有关
      <code class="filename">sockets.target</code> 的解释，详见
      <a href="systemd.special.html#"><span class="citerefentry"><span class="refentrytitle">systemd.special</span>(7)</span></a> 手册。
      如果某 socket 单元已被包含在 <code class="filename">sockets.target</code> 中，
      那么不建议在其中再添加任何额外的依赖关系(例如
      <code class="filename">multi-user.target</code>
      之类)。</p></div><div class="refsect2"><a name="id-1.5.5"></a><h3 id="基于 D-Bus 的启动">基于 D-Bus 的启动<a class="headerlink" title="Permalink to this headline" href="daemon.html#%E5%9F%BA%E4%BA%8E%20D-Bus%20%E7%9A%84%E5%90%AF%E5%8A%A8">¶</a></h3><p>如果守护进程使用 D-Bus 与客户端通信，
      那么它应该使用基于 D-Bus 的启动机制，
      这样当客户端访问其 D-Bus 接口时，该服务将被自动启动。
      该机制是通过 D-Bus service 文件实现的(不要与普通的单元文件混淆)。
      为了确保让 D-Bus 使用 systemd 来启动与维护守护进程，
      必须在这些 D-Bus service 文件中使用
      <code class="varname">SystemdService=</code> 指明其匹配的服务单元。
      例如，对于文件名为
       <code class="filename">org.freedesktop.RealtimeKit.service</code> 的
       D-Bus service 来说，
      为了将其绑定到 <code class="filename">rtkit-daemon.service</code> 服务单元，
      必须确保在该文件中设置了
      <code class="varname">SystemdService=rtkit-daemon.service</code> 指令。
      注意，必须明确设置
       <code class="varname">SystemdService=</code> 指令，
      否则当服务单元同时使用多种启动机制时，
      可能会导致竞争条件的出现。</p></div><div class="refsect2"><a name="id-1.5.6"></a><h3 id="基于设备的启动">基于设备的启动<a class="headerlink" title="Permalink to this headline" href="daemon.html#%E5%9F%BA%E4%BA%8E%E8%AE%BE%E5%A4%87%E7%9A%84%E5%90%AF%E5%8A%A8">¶</a></h3><p>用于管理特定类型硬件的守护进程，
      只应该在符合条件的硬件变为可用或者被插入时，
      才需要启动。
      为了达到上述目的，
      可以将服务的启动/停止与硬件的插入/拔出事件绑定。
      当带有 "<code class="literal">systemd</code>" 标签的设备出现在 sysfs/udev 设备树中时，
      systemd 将会自动为其创建对应的 device 单元。
      通过向这些单元中添加对其他单元的 <code class="varname">Wants=</code> 依赖，
      就可以实现当该 device 单元被启动(也就是硬件被插入)时，
      连带启动其他单元，从而实现基于设备的启动。
      这可以通过向 udev 规则库中添加 <code class="varname">SYSTEMD_WANTS=</code> 属性来实现，
      详见 <a href="systemd.device.html#"><span class="citerefentry"><span class="refentrytitle">systemd.device</span>(5)</span></a> 手册。
      通常，并不是将 service 单元直接添加到设备的 <code class="varname">Wants=</code> 依赖中，
      而是通过专用的 target 单元间接添加。
      例如，不是将 <code class="filename">bluetoothd.service</code>
      添加到各种蓝牙设备的 <code class="varname">Wants=</code> 依赖中，
      而是将 <code class="filename">bluetoothd.service</code>
      添加到 bluetooth.target 的 <code class="varname">Wants=</code> 依赖中，
      同时再将 bluetooth.target 添加到各种蓝牙设备的 <code class="varname">Wants=</code> 依赖中。
      通过引入 bluetooth.target 这个抽象层，
      系统管理员无需批量修改 udev 规则库，
      仅通过 <span class="command"><strong>systemctl enable|disable …</strong></span> 命令
      修改 <code class="filename">bluetooth.target.wants/</code> 目录中的软链接，
      即可控制 <code class="filename">bluetoothd.service</code>
      的使用。</p></div><div class="refsect2"><a name="id-1.5.7"></a><h3 id="基于路径的启动">基于路径的启动<a class="headerlink" title="Permalink to this headline" href="daemon.html#%E5%9F%BA%E4%BA%8E%E8%B7%AF%E5%BE%84%E7%9A%84%E5%90%AF%E5%8A%A8">¶</a></h3><p>对于处理 spool 文件或目录的
      守护进程(例如打印服务)来说，
      仅在 spool 文件或目录状态发生变化或者内容非空时，
      才需要启动。
      通过 <code class="filename">.path</code> 单元实现的、
      基于路径的启动机制正好适用于这种场合，
      详见
      <a href="systemd.path.html#"><span class="citerefentry"><span class="refentrytitle">systemd.path</span>(5)</span></a> 手册。</p></div><div class="refsect2"><a name="id-1.5.8"></a><h3 id="基于定时器的启动">基于定时器的启动<a class="headerlink" title="Permalink to this headline" href="daemon.html#%E5%9F%BA%E4%BA%8E%E5%AE%9A%E6%97%B6%E5%99%A8%E7%9A%84%E5%90%AF%E5%8A%A8">¶</a></h3><p>对于周期性的操作
      (例如垃圾文件清理或者网络对时)，
      可以通过基于定时器的启动机制来实现。
      这种机制通过 <code class="filename">.timer</code> 单元实现，详见
      <a href="systemd.timer.html#"><span class="citerefentry"><span class="refentrytitle">systemd.timer</span>(5)</span></a> 手册。</p></div><div class="refsect2"><a name="id-1.5.9"></a><h3 id="其他启动方式">其他启动方式<a class="headerlink" title="Permalink to this headline" href="daemon.html#%E5%85%B6%E4%BB%96%E5%90%AF%E5%8A%A8%E6%96%B9%E5%BC%8F">¶</a></h3><p>在其他操作系统上还存在着其他的启动机制，
      不过这些机制都可以被前述的各种机制的组合替代。因此在这里不再赘述。
      </p></div></div><div class="refsect1"><a name="id-1.6"></a><h2 id="与 Systemd 整合">与 Systemd 整合<a class="headerlink" title="Permalink to this headline" href="daemon.html#%E4%B8%8E%20Systemd%20%E6%95%B4%E5%90%88">¶</a></h2><div class="refsect2"><a name="id-1.6.2"></a><h3 id="编写 systemd 单元文件">编写 systemd 单元文件<a class="headerlink" title="Permalink to this headline" href="daemon.html#%E7%BC%96%E5%86%99%20systemd%20%E5%8D%95%E5%85%83%E6%96%87%E4%BB%B6">¶</a></h3><p>在编写单元文件时
      应当考虑下列建议：</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>尽可能不用
        <code class="varname">Type=forking</code> 。
        若非用不可，则必须正确设置
        <code class="varname">PIDFile=</code> 指令。参见
        <a href="systemd.service.html#"><span class="citerefentry"><span class="refentrytitle">systemd.service</span>(5)</span></a>
        手册。</p></li><li class="listitem"><p>若守护进程在 D-Bus 上注册了一个名字，
        则应尽可能使用 <code class="varname">Type=dbus</code>
        类型的服务。</p></li><li class="listitem"><p>设置一个
        易于理解的
        <code class="varname">Description=</code></p></li><li class="listitem"><p>确保
        <code class="varname">DefaultDependencies=yes</code> ，
        除非
        该单元必须在系统启动的早期启动或者必须在系统关闭的末期关闭。</p></li><li class="listitem"><p>通常无需显式定义依赖关系。
        不过，如果确实需要显式定义依赖关系，
        为了确保单元文件不局限于特定的发行版，仅应该依赖于
        <a href="systemd.special.html#"><span class="citerefentry"><span class="refentrytitle">systemd.special</span>(7)</span></a>
        中列出的单元
        以及自身所属软件包中提供的单元。</p></li><li class="listitem"><p>确保在
        "<code class="literal">[Install]</code>" 小节中
        包含完整的启用信息(参见
        <a href="systemd.unit.html#"><span class="citerefentry"><span class="refentrytitle">systemd.unit</span>(5)</span></a> 手册)。
        若希望自动启动该单元，
        则应该设置 <code class="varname">WantedBy=multi-user.target</code> 或
        <code class="varname">WantedBy=graphical.target</code>
        若希望自动启动该单元的套接字，则应该设置
        <code class="varname">WantedBy=sockets.target</code> 。
        通常你还希望在启用该单元时，
        一起启用对应的套接字单元
        (假定为 <code class="filename">foo.service</code>)，
        因此还应该设置
        <code class="varname">Also=foo.socket</code></p></li></ol></div></div><div class="refsect2"><a name="id-1.6.3"></a><h3 id="安装 service 单元文件">安装 service 单元文件<a class="headerlink" title="Permalink to this headline" href="daemon.html#%E5%AE%89%E8%A3%85%20service%20%E5%8D%95%E5%85%83%E6%96%87%E4%BB%B6">¶</a></h3><p>当从源代码编译安装(<span class="command"><strong>make
      install</strong></span>)软件包时，
      其中的系统服务单元文件会被默认安装到
      <span class="command"><strong>pkg-config systemd --variable=systemdsystemunitdir</strong></span>
      命令返回的目录中(通常是 <code class="filename">/usr/lib/systemd/system</code>)；
      而其中的用户服务单元文件会被默认安装到
      <span class="command"><strong>pkg-config systemd --variable=systemduserunitdir</strong></span>
      命令返回的目录中(通常是 <code class="filename">/usr/lib/systemd/user</code>)；
      但并不应该使用 <span class="command"><strong>systemctl enable …</strong></span> 命令启用它们。
      当从包管理器安装(<span class="command"><strong>rpm -i</strong></span>)二进制软件包时，
      其中的单元文件应该同样安装到上述位置。
      但不同之处在于，
      还应该使用 <span class="command"><strong>systemctl enable …</strong></span> 命令启用它们，
      因此安装的单元
      有可能会在开机时自动启动。</p><p>使用
      <a href="https://linux.die.net/man/1/autoconf"><span class="citerefentry"><span class="refentrytitle">autoconf</span>(1)</span></a>
      的软件包可以在源代码配置期间
      使用包含类似如下片段的配置脚本
      来确定
      单元文件的安装路径：</p><pre class="programlisting">PKG_PROG_PKG_CONFIG
AC_ARG_WITH([systemdsystemunitdir],
     [AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],,
     [with_systemdsystemunitdir=auto])
AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [
     def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)

     AS_IF([test "x$def_systemdsystemunitdir" = "x"],
   [AS_IF([test "x$with_systemdsystemunitdir" = "xyes"],
    [AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])])
    with_systemdsystemunitdir=no],
   [with_systemdsystemunitdir="$def_systemdsystemunitdir"])])
AS_IF([test "x$with_systemdsystemunitdir" != "xno"],
      [AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])])
AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"])</pre><p>无论系统中
      是否存在 systemd ，
      上述配置片段都允许自动安装单元文件。
      (上述配置片段中
      并不包含用户单元目录，
      怎样修改以加入用户单元目录，
      就留给读者作为练习题吧)</p><p>为了确保
      <span class="command"><strong>make distcheck</strong></span> 正常工作，
      建议将下面的内容添加到
      <a href="https://linux.die.net/man/1/automake"><span class="citerefentry"><span class="refentrytitle">automake</span>(1)</span></a>
      项目顶层目录的
       <code class="filename">Makefile.am</code>
      文件中：</p><pre class="programlisting">AM_DISTCHECK_CONFIGURE_FLAGS = \
  --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)</pre><p>最后，应该使用类似下面的 automake 配置片段来安装单元文件：</p><pre class="programlisting">if HAVE_SYSTEMD
systemdsystemunit_DATA = \
  foobar.socket \
  foobar.service
endif</pre><p>在
      <a href="https://linux.die.net/man/8/rpm"><span class="citerefentry"><span class="refentrytitle">rpm</span>(8)</span></a>
      <code class="filename">.spec</code> 文件中，
      可以使用下面的配置片段在安装/卸载时启用/禁用服务单元。
      这个示例中使用了由 systemd 提供的 RPM 宏。
      对于非红帽系的其他发行版，
      请阅读相应的打包指南，
      以详细了解其软件包管理器的相应等价替代。</p><p>在文件头部：</p><pre class="programlisting">BuildRequires: systemd
%{?systemd_requires}</pre><p>下面是脚本：</p><pre class="programlisting">%post
%systemd_post foobar.service foobar.socket

%preun
%systemd_preun foobar.service foobar.socket

%postun
%systemd_postun</pre><p>如果在软件包升级时需要重启服务，
      应该将上面的 "<code class="literal">%postun</code>" 脚本
      替换为：</p><pre class="programlisting">%postun
%systemd_postun_with_restart foobar.service</pre><p>注意， "<code class="literal">%systemd_post</code>" 与
      "<code class="literal">%systemd_preun</code>"
      的参数是将被安装/卸载的单元名称列表(空格分隔)。
      "<code class="literal">%systemd_postun</code>" 不需要参数。
      "<code class="literal">%systemd_postun_with_restart</code>"
      的参数是将被重启的单元名称列表(空格分隔)。</p><p>若想
      从只附带 SysV init 脚本的老版本软件包升级到
      同时附带 SysV init 脚本与原生 systemd 服务单元的新版本软件包，
      可以使用类似下面的片段：</p><pre class="programlisting">%triggerun -- foobar &lt; 0.47.11-1
if /sbin/chkconfig --level 5 foobar ; then
  /bin/systemctl --no-reload enable foobar.service foobar.socket &gt;/dev/null 2&gt;&amp;1 || :
fi</pre><p>本例中的 0.47.11-1 是第一个包含原生服务单元的版本号。
      上述片段保证了首次安装单元文件时，
      当且仅当原有的 SysV init 脚本已被启用的情况下，
      新的原生服务单元才会被启用，
      这样就确保了版本升级不会破坏服务的启用/禁用状态。注意，
      <span class="command"><strong>chkconfig</strong></span> 是红帽系发行版专有的命令，
      用于检查 SysV init 脚本是否被启用。
      其他发行版一般也都有自己专属的
      功能类似的命令。</p></div></div><div class="refsect1"><a name="id-1.7"></a><h2 id="移植已有的守护进程">移植已有的守护进程<a class="headerlink" title="Permalink to this headline" href="daemon.html#%E7%A7%BB%E6%A4%8D%E5%B7%B2%E6%9C%89%E7%9A%84%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B">¶</a></h2><p>虽然 systemd 兼容
    传统的 SysV 初始化系统，
    但是移植旧有的守护进程可以更好的利用 systemd 的先进特性。
    建议对旧有的 SysV 守护进程做如下改进：
    …[省略]…</p></div><div class="refsect1"><a name="id-1.8"></a><h2 id="放置守护进程的数据">放置守护进程的数据<a class="headerlink" title="Permalink to this headline" href="daemon.html#%E6%94%BE%E7%BD%AE%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B%E7%9A%84%E6%95%B0%E6%8D%AE">¶</a></h2><p>建议遵守
    <a href="file-hierarchy.html#"><span class="citerefentry"><span class="refentrytitle">file-hierarchy</span>(7)</span></a>
    所建议的通用准则。</p></div><div class="refsect1"><a name="id-1.9"></a><h2 id="参见">参见<a class="headerlink" title="Permalink to this headline" href="daemon.html#%E5%8F%82%E8%A7%81">¶</a></h2><p>
      <a href="systemd.html#"><span class="citerefentry"><span class="refentrytitle">systemd</span>(1)</span></a>,
      <a href="http://www.jinbuguo.com/systemd/sd-daemon.html#"><span class="citerefentry"><span class="refentrytitle">sd-daemon</span>(3)</span></a>,
      <a href="http://www.jinbuguo.com/systemd/sd_listen_fds.html#"><span class="citerefentry"><span class="refentrytitle">sd_listen_fds</span>(3)</span></a>,
      <a href="http://www.jinbuguo.com/systemd/sd_notify.html#"><span class="citerefentry"><span class="refentrytitle">sd_notify</span>(3)</span></a>,
      <a href="daemon.html#"><span class="citerefentry"><span class="refentrytitle">daemon</span>(3)</span></a>,
      <a href="systemd.service.html#"><span class="citerefentry"><span class="refentrytitle">systemd.service</span>(5)</span></a>,
      <a href="file-hierarchy.html#"><span class="citerefentry"><span class="refentrytitle">file-hierarchy</span>(7)</span></a>
    </p></div></div></body></html>
